using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Wrath.Windows.Forms;
using System.IO;
using Wrath.Xml;
using Wrath.RuleForge.Property;
using Wrath.RuleForge.Interfaces;

namespace Wrath.RuleForge.RuleSets
{
    public partial class RuleSetExplorer : DockContent
    {
        private string basePath = "";
        private string ruleSetName = "";

        #region Common Nodes

        private TreeNode rootNode;
        private TreeNode includeFileNode;
        private TreeNode modulesNode;
        private TreeNode scriptsNode;
        private TreeNode propertyNode;

        #endregion

        public RuleSetExplorer()
        {
            InitializeComponent();
            EventBroker.RuleSetOpen += new EventBroker.PathEventHandler(EventBroker_RuleSetOpen);

            EventBroker.ElementPropertyChanged += new EventHandler(EventBroker_ElementPropertyChanged);

            EventBroker.RuleSetClose += new EventHandler(EventBroker_RuleSetClose);
        }

        void EventBroker_RuleSetClose(object sender, EventArgs e)
        {
            basePath = "";
            ruleSetName = "";
            CurrentRuleSet.Path = null;

            rootNode = null;
            includeFileNode = null;
            modulesNode = null;
            scriptsNode = null;
            propertyNode = null;

            treeView.Nodes.Clear();
        }

        void EventBroker_ElementPropertyChanged(object sender, EventArgs e)
        {
            if (treeView.SelectedNode != null)
            {
                if (treeView.SelectedNode.Tag is Element)
                {
                    Element element = (Element)treeView.SelectedNode.Tag;
                    if (element.Name == "includefile")
                    {
                        string key = treeView.SelectedNode.ImageKey;
                        if (key.IndexOf("_modified") < 0)
                        {
                            if (imageList.Images.ContainsKey(key + "_modified"))
                            {
                                treeView.SelectedNode.ImageKey += "_modified";
                                treeView.SelectedNode.SelectedImageKey += "_modified";
                            }
                        }
                    }
                    else
                    {
                        if (treeView.SelectedNode.Parent.Tag is Element)
                        {
                            Element parentElement = (Element)treeView.SelectedNode.Parent.Tag;

                            if (parentElement.Name == "includefile")
                            {
                                string key = treeView.SelectedNode.Parent.ImageKey;
                                if (key.IndexOf("_modified") < 0)
                                {
                                    if (imageList.Images.ContainsKey(key + "_modified"))
                                    {
                                        treeView.SelectedNode.Parent.ImageKey += "_modified";
                                        treeView.SelectedNode.Parent.SelectedImageKey += "_modified";
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        void EventBroker_RuleSetOpen(EventBroker.PathEventArgs pathEventArgs)
        {
            basePath = pathEventArgs.Path;
            ruleSetName = Path.GetFileName(basePath);

            // Reinitialize the CurrentRuleSet class.
            CurrentRuleSet.Path = basePath;

            InitializeTreeView();

            FileBroker.Files.Clear();

            LoadBaseFile();

            LoadIncludeFiles(FileBroker.Files["base.xml"], includeFileNode);

            LoadScriptFiles();

            LoadModules();

            rootNode.Expand();
        }

        private void LoadBaseFile()
        {
            // Load base.xml
            try
            {
                FileBroker.Files.Add("base.xml", new FileParser(basePath + "\\base.xml"));
            }
            catch (Exception e)
            {
                FileBroker.Debug.WriteLine("Error opening base.xml:\n{0}", e.Message);
                Exception newEx = new Exception("Error opening base.xml", e);
                FileBroker.LoadingExceptions.Add(newEx);
                return;
            }

            // Link DesktopImage
            Element desktopFrame = FileBroker.Files["base.xml"].FindFirstElement("desktopframe");
            TreeNode desktopNode = new TreeNode("Desktop");
            desktopNode.Name = "Desktop";
            desktopNode.ImageKey = "image";
            desktopNode.SelectedImageKey = "image";
            desktopNode.Tag = desktopFrame;
            propertyNode.Nodes.Add(desktopNode);

            // Link TokenRoot
            Element tokenRoot = FileBroker.Files["base.xml"].FindFirstElement("tokenroot");
            TreeNode tokenNode = new TreeNode("Token Root");
            tokenNode.Name = "TokenRoot";
            tokenNode.ImageKey = "folder";
            tokenNode.SelectedImageKey = "folder";
            tokenNode.Tag = tokenRoot;
            propertyNode.Nodes.Add(tokenNode);

            // Load importinfo
            Element importinfo = FileBroker.Files["base.xml"].FindFirstElement("importinfo");
            TreeNode importNode = new TreeNode("Import Info");
            importNode.Name = "ImportInfo";
            importNode.ImageKey = "database";
            importNode.SelectedImageKey = "database";
            importNode.Tag = importinfo;
            propertyNode.Nodes.Add(importNode);

            List<Element> imports = FileBroker.Files["base.xml"].FindAll("acceptfrom", "ruleset");
            foreach (Element import in imports)
            {
                Wrath.Xml.Attribute rulesetAttribute = new Wrath.Xml.Attribute();
                rulesetAttribute = import.FindAttribute("ruleset");
                if (rulesetAttribute != null)
                {
                    TreeNode importChild = new TreeNode(rulesetAttribute.Value);
                    importChild.Name = "Import_" + rulesetAttribute.Value;
                    importChild.ImageKey = "ruleset";
                    importChild.SelectedImageKey = "ruleset";
                    importChild.Tag = import;
                    importNode.Nodes.Add(importChild);
                }
            }
        }

        private void LoadIncludeFiles(FileParser file, TreeNode parentNode)
        {
            if (file == null) return;

            // Load include files
            List<Element> includeFiles = file.FindAll("includefile");
            if (includeFiles != null)
            {
                foreach (Element element in includeFiles)
                {
                    Wrath.Xml.Attribute source = element.FindAttribute("source");
                    if (source != null)
                    {
                        string sourceFile = source.Value;

                        string filePath = basePath + "\\" + source.Value;

                        try
                        {
                            if (File.Exists(filePath))
                            {
                                FileParser parser = new FileParser(filePath);
                                FileBroker.Files.Add(sourceFile, parser);

                                // Add TreeNode
                                TreeNode includeNode = new TreeNode(Path.GetFileName(filePath));
                                includeNode.Name = "includefile";
                                includeNode.ImageKey = "includefile";
                                includeNode.SelectedImageKey = "includefile";
                                includeNode.Tag = element;
                                parentNode.Nodes.Add(includeNode);

                                // Parse Include Files
                                ParseIncludeFile(element, includeNode.Nodes);
                            }
                        }
                        catch (Exception e)
                        {
                            FileBroker.Debug.WriteLine("Error opening {0}:\n{1}", sourceFile, e.Message);
                            Exception newEx = new Exception("Error opening " + sourceFile, e);
                            FileBroker.LoadingExceptions.Add(newEx);
                            continue;
                        }
                    }
                }
            }// if
        }

        private void LoadScriptFiles()
        {
            if (FileBroker.Files["base.xml"] == null) return;

            // Load include files
            List<Element> scriptFiles = FileBroker.Files["base.xml"].FindAll("script");
            if (scriptFiles != null)
            {
                foreach (Element element in scriptFiles)
                {
                    string scriptName = "script [no name attribute]";
                    Wrath.Xml.Attribute source = element.FindAttribute("name");
                    if (source != null)
                    {
                        scriptName = source.Value;
                    }

                    // Add TreeNode
                    TreeNode scriptNode = new TreeNode(scriptName);
                    scriptNode.Name = "script";
                    scriptNode.ImageKey = "script";
                    scriptNode.SelectedImageKey = "script";
                    scriptNode.Tag = element;
                    scriptsNode.Nodes.Add(scriptNode);
                }
            }// if
        }

        private void LoadModules()
        {
            for (int i = 0; i < Globals.Modules.Count; i++)
            {
                Globals.ModuleInformation mi = Globals.Modules.GetModule(i);
                if (mi.RuleSet == ruleSetName)
                {
                    // Add TreeNode
                    TreeNode moduleNode = new TreeNode(mi.Name);
                    moduleNode.Name = "module";
                    moduleNode.ImageKey = "module";
                    moduleNode.SelectedImageKey = "module";
                    moduleNode.Tag = mi;
                    modulesNode.Nodes.Add(moduleNode);
                }
            }
        }

        private void InitializeTreeView()
        {
            treeView.Nodes.Clear();

            rootNode = new TreeNode(ruleSetName);
            rootNode.Name = "Root";
            rootNode.ImageKey = "ruleset";
            rootNode.SelectedImageKey = "ruleset";

            treeView.Nodes.Add(rootNode);

            propertyNode = new TreeNode("Properties");
            propertyNode.Name = "Properties";
            propertyNode.ImageKey = "propertygroup";
            propertyNode.SelectedImageKey = "propertygroup";

            rootNode.Nodes.Add(propertyNode);

            includeFileNode = new TreeNode("Include Files");
            includeFileNode.Name = "IncludeFiles";
            includeFileNode.ImageKey = "includefilegroup";
            includeFileNode.SelectedImageKey = "includefilegroup";

            rootNode.Nodes.Add(includeFileNode);

            scriptsNode = new TreeNode("Scripts");
            scriptsNode.Name = "Scripts";
            scriptsNode.ImageKey = "scriptgroup";
            scriptsNode.SelectedImageKey = "scriptgroup";

            rootNode.Nodes.Add(scriptsNode);

            modulesNode = new TreeNode("Modules");
            modulesNode.Name = "Modules";
            modulesNode.ImageKey = "modulegroup";
            modulesNode.SelectedImageKey = "modulegroup";

            rootNode.Nodes.Add(modulesNode);
        }

        /// <summary>
        /// An event that gets fired after a TreeNode is selected.
        /// </summary>
        /// <param name="sender">Probably the TreeView</param>
        /// <param name="e">Information about the selected TreeNode.</param>
        private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Tag is Element)
            {
                IPropertyWrapper<Element> propertyWrapper;
                Element element = (Element)e.Node.Tag;

                propertyWrapper = ElementPropertyWrapperProvider.CreateInstance(element);

                if (element.Name.ToLower() == "includefile")
                {
                    ParseIncludeFile(element, e.Node.Nodes);
                }
                else if (element.Name.ToLower() == "icon")
                {
                    Wrath.Xml.Attribute source = element.FindAttribute("file");
                    if (source != null)
                    {
                        string filePath = basePath + "\\" + source.Value.Replace("/", "\\");

                        EventBroker.PathEventArgs pathArgs = new EventBroker.PathEventArgs(filePath);
                        EventBroker.FireIconSelected(pathArgs);
                    }
                }

                // Load Element Property Sheet
                EventBroker.ElementPropertyWrapperEventArgs args = new EventBroker.ElementPropertyWrapperEventArgs(propertyWrapper);
                EventBroker.FireRuleSetElementSelected(args);
            }
            else
            {
                // Clear Element Property Sheet
                EventBroker.ElementPropertyWrapperEventArgs args = new EventBroker.ElementPropertyWrapperEventArgs(null);
                EventBroker.FireRuleSetElementSelected(args);
            }
        }

        private void ParseIncludeFile(Element element, TreeNodeCollection treeNodeCollection)
        {
            if (treeNodeCollection.Count < 1)
            {
                Wrath.Xml.Attribute source = element.FindAttribute("source");
                if (source != null)
                {
                    FileParser includeFile;
                    if (!FileBroker.Files.ContainsKey(source.Value))
                    {
                        string sourceFile = source.Value;
                        string filePath = basePath + "\\" + source.Value.Replace("/", "\\");

                        includeFile = new FileParser(filePath);
                        FileBroker.Files.Add(sourceFile, includeFile);
                    }
                    else
                    {
                        includeFile = FileBroker.Files[source.Value];
                    }

                    // Find the root node.
                    Element rootNode = includeFile.Root.FindFirstElement("root", false);
                    if (rootNode == null) return;

                    foreach (Element child in rootNode.ChildElements)
                    {
                        treeNodeCollection.Add(CreateElementNode(child));
                    }
                }
            }
        }

        private TreeNode CreateElementNode(Element element)
        {
            TreeNode node = new TreeNode();

            if (element.Type == ElementType.Element)
            {
                node.Name = element.Name;
                node.ImageKey = element.Name;
                node.SelectedImageKey = element.Name;
                node.Tag = element;
                node.ToolTipText = element.Name;

                if (element.Name.ToLower() == "includefile")
                {
                    Wrath.Xml.Attribute source = element.FindAttribute("source");
                    if (source != null)
                    {
                        node.Text = source.Value;
                    }
                    else
                    {
                        node.Text = "includefile [no source attribute]";
                    }

                    // Parse additional include files.
                    ParseIncludeFile(element, node.Nodes);
                }
                else if (element.Name.ToLower() == "script")
                {
                    Wrath.Xml.Attribute source = element.FindAttribute("file");
                    if (source != null)
                    {
                        node.Text = Path.GetFileName(source.Value.Replace("/", "\\"));
                    }
                    else
                    {
                        node.Text = "script [no file attribute]";
                    }
                }
                else if (element.Name.ToLower() == "framedef")
                {
                    // Get frame name
                    Wrath.Xml.Attribute source = element.FindAttribute("name");
                    if (source != null)
                    {
                        node.Text = source.Value;
                    }
                    else
                    {
                        node.Text = element.Name + " [no name attribute]";
                    }

                    // Add frame to global collection.
                    CurrentRuleSet.Frames.Add(source.Value, element);

                }
                else if (element.Name.ToLower() == "icon")
                {
                    // Get icon name
                    Wrath.Xml.Attribute source = element.FindAttribute("name");
                    if (source != null)
                    {
                        node.Text = source.Value;
                        
                        // Add icon to global collection.
                        CurrentRuleSet.Icons.Add(source.Value, element);
                    }
                    else
                    {
                        node.Text = element.Name + " [no name attribute]";
                    }

                }
                else if (element.Name.ToLower() == "template")
                {
                    Wrath.Xml.Attribute source = element.FindAttribute("name");
                    if (source != null)
                    {
                        node.Text = source.Value;

                        // Add to global templates list.
                        CurrentRuleSet.Templates.Add(source.Value, element);
                    }
                    else
                    {
                        node.Text = element.Name + " [no name attribute]";
                    }                    
                }
                else if (element.Name.ToLower() == "windowclass"
                || element.Name.ToLower() == "font"
                || element.Name.ToLower() == "panel"
                || element.Name.ToLower() == "portraitset"
                || element.Name.ToLower() == "die"
                )
                {
                    Wrath.Xml.Attribute source = element.FindAttribute("name");
                    if (source != null)
                    {
                        node.Text = source.Value;
                    }
                    else
                    {
                        node.Text = element.Name + " [no name attribute]";
                    }
                }
                else
                {
                    node.Text = element.Name;
                }
            }
            else if (element.Type == ElementType.Comment)
            {
                node.Name = "comment";
                node.ImageKey = "comment";
                node.SelectedImageKey = "comment";
                node.Tag = element;
                node.ToolTipText = element.Value;
                node.Text = element.Value;
                node.ForeColor = Color.CornflowerBlue;
            }

            return node;
        }

        private void treeView_DoubleClick(object sender, EventArgs e)
        {
            if (treeView.SelectedNode.Tag is Element)
            {
                Element element = (Element)treeView.SelectedNode.Tag;

                if (element.Name.ToLower() == "framedef")
                {
                    EventBroker.ElementEventArgs args = new EventBroker.ElementEventArgs(element);
                    EventBroker.FireFrameDoubleClicked(args);
                }
                else if (element.Name.ToLower() == "windowclass")
                {
                    EventBroker.ElementEventArgs args = new EventBroker.ElementEventArgs(element);
                    EventBroker.FireWindowClassDoubleClicked(args);
                }
                else if (element.Name.ToLower() == "customdie")
                {
                    EventBroker.ElementEventArgs args = new EventBroker.ElementEventArgs(element);
                    EventBroker.FireCustomDieDoubleClicked(args);
                }
            }
        }
    }
}